package net.frenopatico.kynes.dao.jdo;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;

import javax.jdo.PersistenceManagerFactory;

import net.frenopatico.kynes.dao.GenericDao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.orm.jdo.JdoObjectRetrievalFailureException;
import org.springframework.orm.jdo.support.JdoDaoSupport;

import com.google.appengine.api.datastore.Key;

/**
 * Basic CRUD operations for T entities with PK id field for the Google App
 * engine JDO.
 * 
 * @author Eduardo Ramírez <edu.kynes@gmail.com>
 * 
 * @param <T>
 *            type of the entities to persist.
 * @param <PK>
 *            type of the T's id field.
 */
public abstract class AbstractJdoGenericDao<T, PK extends Serializable> extends JdoDaoSupport implements GenericDao<T, PK> {
    /**
     * Class to persist in the datastore.
     */
    private final transient Class<T> persistentClass;

    /**
     * Persistence Manager Factory injector.
     * 
     * @param pmf static Persistence Manager Factory
     * 
     */
    @Autowired
    protected final void setPMF( final PersistenceManagerFactory pmf ) {
    	this.setPersistenceManagerFactory( pmf  );
    }
    
    /**
     * Constructor that obtains the class to easy DAO creation.
     * 
     */
    @SuppressWarnings("unchecked")
    public AbstractJdoGenericDao() {
        super();
        
        // We obtain the type of class to persist from the first Generic Parameter of the ancestor class.
        ParameterizedType paramType = (ParameterizedType) getClass().getGenericSuperclass();
        this.persistentClass = (Class<T>) paramType.getActualTypeArguments()[0];
    }

    /*
     * (non-Javadoc)
     * 
     * @see net.frenopatico.kynes.dao.GenericDao#exists(java.io.Serializable)
     */
    @Override
    public final boolean exists( final PK entityId ) {
    	boolean exists = true; //NOPMD
    	
        try {
			this.get(entityId);
		} catch ( ObjectRetrievalFailureException e ) {
			exists = false;
		}
		
		return exists;
    }

    /*
     * (non-Javadoc)
     * 
     * @see net.frenopatico.kynes.dao.GenericDao#get( java.io.Serializable )
     */
    @Override
    public final T get( final PK entityId ) {
        T entity;

		try {
			entity = (T) getJdoTemplate().getObjectById( this.persistentClass, entityId ); //NOPMD
		} catch ( JdoObjectRetrievalFailureException e ) {
			throw new ObjectRetrievalFailureException( this.persistentClass, entityId );
		}

        return entity;
    }

    /*
     * (non-Javadoc)
     * 
     * @see net.frenopatico.kynes.dao.GenericDao#getByKey( com.google.appengine.api.datastore.Key )
     */
    @Override
    public final T getByKey( final Key entityId ) {
    	T entity;

		try {
			entity = (T) getJdoTemplate().getObjectById( this.persistentClass, entityId ); //NOPMD
		} catch ( JdoObjectRetrievalFailureException e ) {
			throw new ObjectRetrievalFailureException( this.persistentClass, entityId );
		}

        return entity;
    }

    /*
     * (non-Javadoc)
     * 
     * @see net.frenopatico.kynes.dao.GenericDao#getAll()
     */
    @Override
    public final List<T> getAll() {
        return new ArrayList<T>( getJdoTemplate().find( this.persistentClass ) );
    }

    /*
     * (non-Javadoc)
     * 
     * @see net.frenopatico.kynes.dao.GenericDao#getAllDistinct()
     */
    @Override
    public final List<T> getAllDistinct() {
        Collection<T> result = new LinkedHashSet<T>( this.getAll() );
        return new ArrayList<T>( result );
    }

    /*
     * (non-Javadoc)
     * 
     * @see net.frenopatico.kynes.dao.GenericDao#remove(java.io.Serializable)
     */
    @Override
    public final void remove( final PK entityId ) {
        getJdoTemplate().deletePersistent( this.get( entityId ) );
    }

    /*
     * (non-Javadoc)
     * 
     * @see net.frenopatico.kynes.dao.GenericDao#save(java.lang.Object)
     */
    @Override
    public final T save( final T entity ) {
        return getJdoTemplate().makePersistent( entity );
    }

}
